package com.bytz.modules.cms.common.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.bytz.modules.cms.common.excel.AdaptiveColumnWidthStrategy;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Component
public class ExcelUtil {

    public static void exportTemp(String fileName, String sheetName, Class<?> clz, HttpServletResponse response) throws Exception {

        setDownloadHeader(fileName + ".xlsx", response);

        OutputStream out = response.getOutputStream();
        ExcelWriter excelWriter = EasyExcel.write(out, clz).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();

        excelWriter.write((Collection<?>) null, writeSheet);
        excelWriter.finish();
        out.flush();
        out.close();
    }

    public static <T> void export(String fileName, Class<T> clazz, HttpServletResponse response, List<T> data) throws IOException {
        export(fileName, "Sheet1", clazz, response, data);
    }

    public static <T> void export(String fileName, String sheetName, Class<T> clazz, HttpServletResponse response, List<T> data) throws IOException {
        setDownloadHeader(fileName + ".xlsx", response);
        exportToOutputStream(sheetName, clazz, data, response.getOutputStream());
    }

    public static <T> void exportToOutputStream(String sheetName, Class<T> clazz, List<T> data, OutputStream out) throws IOException {
        ExcelWriter excelWriter = EasyExcel.write(out, clazz)
                .registerWriteHandler(new AdaptiveColumnWidthStrategy())
                .registerWriteHandler(getCommonHorizontalCellStyleStrategy())
                .build();
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();

        excelWriter.write(data, writeSheet);
        excelWriter.finish();
        out.flush();
        out.close();
    }

    // 构建ExcelWriter
    public static <T> ExcelWriter constructWrite(String fileName, Class<T> clazz, HttpServletResponse response) throws  IOException{
        setDownloadHeader(fileName, response);
        return EasyExcel.write(response.getOutputStream(), clazz)
                .registerWriteHandler(new AdaptiveColumnWidthStrategy())
                .registerWriteHandler(getCommonHorizontalCellStyleStrategy())
                .build();
    }

    /**
     * 通过模板创建excel，以解决代码生成excel没有theme文件过不了各种校验的问题
     *
     * @param template 模板路径
     * @param data     数据源
     * @param out      输出流
     */
    public static void exportByTemplate(String template, List<?> data, OutputStream out) throws IOException {
        Resource resource = new ClassPathResource(template);

        ExcelWriter excelWriter = EasyExcel.write(out)
                .withTemplate(resource.getInputStream()) // 利用模板的输出流
                .build();
        // 写入模板文件的第一个sheet 索引0
        WriteSheet writeSheet = EasyExcel.writerSheet(0).build();

        // 将数据写入到模板文件的对应sheet中
        excelWriter.write(data, writeSheet);
        excelWriter.finish();
    }

    private static HorizontalCellStyleStrategy getCommonHorizontalCellStyleStrategy() {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置颜色、字体
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 13);
        headWriteCellStyle.setWriteFont(headWriteFont);
        return new HorizontalCellStyleStrategy(headWriteCellStyle, new WriteCellStyle());
    }

    public static void setDownloadHeader(String fileName, HttpServletResponse response) {
        response.addHeader("Content-Disposition", String.format("attachment;fileName=%s", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)));
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
    }

    public static <T> List<T> importGroup(HttpServletRequest request, Class<T> clz, ReadListener<T> readListener) {

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile file = multipartRequest.getFile("file");

        if (file == null) {
            return new ArrayList<>();
        }

        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return EasyExcel.read(inputStream, clz, readListener).sheet(0).doReadSync();
    }

    public static Map<String, List<?>> importGroupBySheetName(HttpServletRequest request, Map<String, Class<?>> sheetClassMap) {

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile file = multipartRequest.getFile("file");

        Map<String, List<?>> result = new HashMap<>();

        if (file == null) {
            return result;
        }

        for (String sheetName : sheetClassMap.keySet()) {
            InputStream inputStream = null;
            try {
                inputStream = file.getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Class<?> clz = sheetClassMap.get(sheetName);
            result.put(
                    sheetName,
                    EasyExcel.read(inputStream, clz, null).sheet(sheetName).doReadSync()
            );
        }
        return result;
    }

    /**
     * 动态修改注解 @ExcelProperty 主要用于修改表头
     */
    public static void modifyFiled(Class<?> clazz, Consumer<Map> consumer, String fieldName) throws Exception {
        // 获取实体类字段
        Field field = clazz.getDeclaredField(fieldName);
        // 获取实体类字段的注解类
        ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
        // 将注解类生成一个代理对象
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
        Field annotationValues = invocationHandler.getClass().getDeclaredField("memberValues");
        annotationValues.setAccessible(true);
        Map map = (Map) annotationValues.get(invocationHandler);

        consumer.accept(map);
    }

    public static void reBuildExcelPropertyValue(Class<?> clazz, String title) throws Exception {

        Field[] exportFields = clazz.getDeclaredFields();
        for (Field field : exportFields) {

            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {

                String[] value = annotation.value();

                if (StringUtils.startsWith(value[0], "{") && StringUtils.endsWith(value[0], "}")) {

                    modifyFiled(clazz, map -> {
                        map.put("value",
                                new String[]{StringUtils.replaceEach(String.format(value[0], title),
                                        new String[]{"{", "}"},
                                        new String[]{"", ""}),
                                        value[1]});
                    }, field.getName());
                }
            }
        }
    }

    public static List<List<String>> buildExcelTitle(Class<?> clz, String mixHead) {

        List<List<String>> head = new ArrayList<>();

        Field[] exportFields = clz.getDeclaredFields();

        Map<Integer, List<String>> fieldMap = new LinkedHashMap<>();

        for (Field field : exportFields) {

            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);

            if (annotation != null) {

                String[] value = annotation.value();
                List<String> header = new ArrayList<>();

                for (String val : value) {

                    if (StringUtils.startsWith(val, "{") && StringUtils.endsWith(val, "}")) {
                        String format = String.format(val, mixHead);

                        header.add(StringUtils.replaceEach(format, new String[]{"{", "}"}, new String[]{"", ""}));
                    } else {

                        header.add(val);
                    }
                }

                fieldMap.put(annotation.index(), header);
            }
        }

        List<Integer> keySet = fieldMap.keySet().stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());

        keySet.forEach(key -> {
            head.add(fieldMap.get(key));
        });

        return head;
    }


}
